package client

import (
	"encoding/json"
	"fmt"
	"iot-base/common/fsm"
	"iot-base/common/tunnel/protocol"
	"net"
	"time"
)

var event [6]fsm.FSMEvent

type DefaultAction struct {
	name     string
	callback func(*fsm.FSMInstance) error
}

func (pAction *DefaultAction) GetName() string {
	return pAction.name
}
func (pAction *DefaultAction) SetParams(param interface{}) {

}

func (pAction *DefaultAction) Execute(pInstance *fsm.FSMInstance) error {
	if pAction.callback != nil {
		return pAction.callback(pInstance)
	}
	return nil
}

func init() {
	fmt.Println("--------init----------")

	{

		event[0].Name = "beginTunnel"
		actionNames := [...]string{"requestTunnel"}
		event[0].ActionNames = actionNames[0:]
		ts := [...]fsm.Transition{fsm.Transition{To: "wait", From: ""}}
		event[0].Transitions = ts[0:]
	}
	{
		event[1].Name = "recvTunnelFailed"
		//actionNames := [...]string{"requestTunnel", "recvRequestTunnelACK"}
		//event[1].ActionNames = actionNames[0:]
		ts := [...]fsm.Transition{fsm.Transition{To: "END", From: "wait"}}
		event[1].Transitions = ts[0:]
	}
	{

		event[2].Name = "recvTunnelSucc"
		actionNames := [...]string{"sendProbeToEdge"}
		event[2].ActionNames = actionNames[0:]
		ts := [...]fsm.Transition{fsm.Transition{To: "tunnel", From: "wait"}}
		event[2].Transitions = ts[0:]
	}
	{
		event[3].Name = "tunnelFailed"
		//actionNames := [...]string{"sendProbeToEdge"}
		//event[2].ActionNames = actionNames[0:]
		ts := [...]fsm.Transition{fsm.Transition{To: "END", From: "tunnel"}}
		event[3].Transitions = ts[0:]
	}
	{
		event[4].Name = "tunnelSucc"
		actionNames := [...]string{"beginKCP"}
		event[4].ActionNames = actionNames[0:]
		ts := [...]fsm.Transition{fsm.Transition{To: "END", From: "tunnel"}}
		event[4].Transitions = ts[0:]
	}
	{
		event[5].Name = "timeout"
		//actionNames := [...]string{"beginKCP"}
		//event[4].ActionNames = actionNames[0:]
		ts := [...]fsm.Transition{fsm.Transition{To: "END", From: "*"}}
		event[5].Transitions = ts[0:]
	}

	var ofsm fsm.FSM
	ofsm.SName = "t1"
	ofsm.UID = "t1"

	ofsm.Body, _ = json.Marshal(event)
	fsm.RegisterFSM(&ofsm)

	fsm.RegisterAction("t1", &DefaultAction{
		name:     "requestTunnel",
		callback: requestTunnel,
	})
	fsm.RegisterAction("t1", &DefaultAction{
		name:     "sendProbeToEdge",
		callback: sendProbeToEdge,
	})
	fsm.RegisterAction("t1", &DefaultAction{
		name:     "beginKCP",
		callback: beginKCP,
	})

	fmt.Println("--------init--------end--")

}

//var pInstance *fsm.FSMInstance
//BeginClientTunnel 创建fsm，为每一个edge建立不同的fsm,t1---edgeid,一个t1包括一个fsmConnection
func BeginClientTunnel(svrAddr string, localPort int, targetID string) (*fsm.FSMInstance, error) {

	//fmt.Println("connect-----", udpAddr, err)

	conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: localPort})

	//var wg sync.WaitGroup
	//wg.Add(1)
	if err == nil {
		fmt.Println("listenudp client----------", conn.LocalAddr())
		pInstance := fsm.CreateFSMInstance("t1")

		pInstance.GetContext().Set("conn", conn)
		pInstance.GetContext().Set("serverAddr", svrAddr)
		//pInstance.GetContext().Set("cport", cport)
		pInstance.GetContext().Set("targetID", targetID)
		//pInstance.GetContext().Set("wg", &wg)
		go pInstance.RunLoop()
		/*pInstance.GetContext().Set("targetIP", targetIP)
		pInstance.GetContext().Set("targetPort", targetPort)
		pInstance.GetContext().Set("netType", netType)
		pInstance.GetContext().Set("wg", &wg)
		fmt.Println("send beginTunnel event")*/
		pInstance.NotifyEvent("beginTunnel")
		return pInstance, nil
	}
	fmt.Println("BeginClientTunnel----------", err)
	return nil, err

	//wg.Wait()

}

func GetEdgeList(addr string) []byte {
	//var client net.UDPConn
	fmt.Println("GetEdgeList---1-----")
	udpAddr, _ := net.ResolveUDPAddr("udp4", addr)
	client, err := net.DialUDP("udp", nil, udpAddr)
	if err == nil {
		var header protocol.Header
		header.Ptype = protocol.GETLIST
		var buf [4096]byte
		nlen, _ := header.ToBuffer(buf[:])
		nlen, err = client.Write(buf[:nlen])
		if err != nil && nlen <= 0 {
			fmt.Println("-------------nlen,err-------", nlen, err)
		}
		client.SetReadDeadline(time.Now().Add(time.Duration(3) * time.Second))
		nLen, _, _ := client.ReadFromUDP(buf[:])
		if nLen > 0 {
			return buf[:nLen]
		}
	}
	fmt.Println("error get edge list--------", err)
	return nil
}
